package com.sh.data.engine.web.aspect;

import com.sh.data.engine.common.annotation.crypto.EncryptField;
import com.sh.data.engine.common.exception.BusinessException;
import com.sh.data.engine.common.util.RSAUtil;
import com.sh.data.engine.web.webapi.controller.base.response.RestResponseEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Objects;


/**
 * @author : fuchen
 * @date : 2021/12/29 16:18
 */
@Order(Ordered.HIGHEST_PRECEDENCE)
@Aspect
@Component
@Slf4j
public class EncryptFieldAop {

    @Pointcut("@annotation(com.sh.data.engine.common.annotation.crypto.EncryptMethod)")
    public void annotationPointCut() {
    }

    @Around("annotationPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        Object responseObj = null;
        try {
            // 取方法中的第一个参数进行解密
            Object requestObj = joinPoint.getArgs()[0];
            handleDecrypt(requestObj);
            responseObj = joinPoint.proceed();
            handleEncrypt(responseObj);
        } catch (BusinessException exception) {
            throw exception;
        } catch (Throwable throwable) {
            log.error(throwable.getMessage(), throwable);
            throw new BusinessException("系统异常");
        }
        return responseObj;
    }

    /**
     * 处理加密
     *
     * @param responseObj
     */
    private void handleEncrypt(Object responseObj) throws Exception {
        if (Objects.isNull(responseObj)) {
            return;
        }

        Object data = null;

        if (responseObj instanceof RestResponseEntity) {
            RestResponseEntity entity = (RestResponseEntity) responseObj;
            data = entity.getData();
        }
        if (data == null) {
            return;
        }

        Field[] fields = data.getClass().getDeclaredFields();
        for (Field field : fields) {
            boolean hasSecureField = field.isAnnotationPresent(EncryptField.class);
            if (hasSecureField) {
                field.setAccessible(true);
                String plaintextValue = (String) field.get(data);
                plaintextValue = URLEncoder.encode(plaintextValue, "UTF-8");
                String encryptValue = RSAUtil.encryptByPublicKey(plaintextValue);
                field.set(data, encryptValue);
            }
        }
    }

    /**
     * 处理解密
     *
     * @param requestObj
     */
    private Object handleDecrypt(Object requestObj) throws Exception {
        if (Objects.isNull(requestObj)) {
            return null;
        }

        Field[] fields = requestObj.getClass().getDeclaredFields();
        for (Field field : fields) {
            boolean hasSecureField = field.isAnnotationPresent(EncryptField.class);
            if (hasSecureField) {
                field.setAccessible(true);
                String encryptValue = (String) field.get(requestObj);
                if (StringUtils.isBlank(encryptValue)) {
                    continue;
                }
                String plaintextValue = RSAUtil.decryptByPrivateKey(encryptValue, "UTF-8");
                plaintextValue = URLDecoder.decode(plaintextValue, "UTF-8");
                field.set(requestObj, plaintextValue);
            }
        }
        return requestObj;
    }
}
